<template>
  <view class="v-image-container" :style="style" @click="clickHandle">
    <image
      v-if="parsedSrc && !error"
      :src="parsedSrc"
      class="v-image"
      :class="{ loaded: loaded, border: border }"
      :lazy-load="lazyLoad"
      :mode="mode"
      @load="loadHandle"
      @error="
        error = true;
        $emit('error');
      "
    />
    <image
      v-else
      class="v-image loaded holder"
      :src="ossMap.get('components-image-placeholder.png')"
      mode="aspectFit"
    />
  </view>
</template>

<script setup lang="ts">
import ossMap from '../../utils/oss-map';
import type { CSSProperties } from 'vue';

const props = withDefaults(
  defineProps<{
    src?: string;
    ossSrc?: string;
    height?: string;
    width?: string;
    borderRadius?: string;
    customStyle?: CSSProperties;
    quality?: number;
    mode?:
      | 'scaleToFill' // 拉伸填充，默认
      | 'aspectFit' // 保持比例，完整显示长边
      | 'aspectFill' // 保持比例，截取至较短边
      | 'widthFix' // 宽度固定，高度自动变化
      | 'heightFix'; // 高度固定，宽度自动变化
    lazyLoad?: boolean;
    border?: boolean;
    preview?: boolean;
  }>(),
  {
    src: undefined,
    ossSrc: undefined,
    height: undefined,
    width: undefined,
    borderRadius: undefined,
    customStyle: undefined,
    mode: undefined,
    quality: undefined,
    lazyLoad: true,
    preview: false,
  },
);

const emits = defineEmits<{
  click: [];
  load: [];
  error: [];
}>();

const loaded = ref(false);
const error = ref(false);

const pageStore = $store.page();
const toPix = (str?: string) => {
  if (!str) return undefined;
  let pix = 0;
  if (str.endsWith('rpx')) {
    pix = parseFloat(str) / pageStore.rpxRatio;
  } else if (str.endsWith('px')) {
    pix = parseFloat(str);
  } else if (str.endsWith('vw')) {
    pix = ((parseFloat(str) / 100) * 750) / pageStore.rpxRatio;
  }
  if (isNaN(pix)) pix = 0;
  return pix || undefined;
};

const originalSrc = computed(() => {
  if (props.src) return props.src;
  if (props.ossSrc) return ossMap.get(props.ossSrc);
  return undefined;
});
const parsedSrc = computed(() => {
  if (!originalSrc.value) return;
  if (originalSrc.value.startsWith('https://file')) {
    const strs: string[] = ['?x-oss-process=image/resize,m_mfit'];
    const height = toPix(props.height);
    const width = toPix(props.width ?? '750rpx');
    let q = props.quality;
    if (q === undefined) {
      const max = Math.max(height ?? 0, width ?? 0);
      q = max > 300 ? 1.8 : 1.3;
    }
    if (height) strs.push(`h_${Math.ceil(height * q)}`);
    if (width) strs.push(`w_${Math.ceil(width * q)}`);
    if (!height && !width) return originalSrc.value;
    return originalSrc.value + strs.join(',');
  }
  return originalSrc.value.replace(/^@\//, '/');
});
const preview = toRef(props, 'preview');
const style = computed(() => {
  const styleObj: CSSProperties = {};
  if (props.height) {
    styleObj.height = props.height;
  }
  if (props.width) {
    styleObj.width = props.width;
  }
  if (props.borderRadius) {
    styleObj.borderRadius = props.borderRadius;
  }
  if (props.customStyle) {
    Object.assign(styleObj, props.customStyle);
  }
  return styleObj;
});

const clickHandle = () => {
  if (preview.value && originalSrc.value && loaded.value) {
    uni.previewImage({ urls: [originalSrc.value] });
  } else {
    emits('click');
  }
};

const loadHandle = () => {
  loaded.value = true;
  emits('load');
};
</script>

<style lang="scss" scoped>
.v-image-container {
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  flex-shrink: 0;
  .v-image {
    width: inherit;
    height: inherit;
    max-height: inherit;
    max-width: inherit;
    display: block;
    opacity: 0;
    transition: opacity 150ms linear;
    &.loaded {
      opacity: 1;
    }
  }
}
.holder {
  box-sizing: border-box;
  background-color: #fafafa;
  border-radius: 8rpx;
}
</style>
